/*
 * Copyright (C) 2010-2014 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
 * limitations under the License.
  */
package trs.gtfs.model;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
* @See https://developers.google.com/transit/gtfs/reference#stop_times_fields
 */
public class TripTimePoint extends Number {
    private static final long serialVersionUID = 8243531115446141672L;
    private final int value;

    public TripTimePoint(int value) {
        this.value = value;
    }

    @Override
    public double doubleValue() {
        return (double) floatValue();
    }

    @Override
    public float floatValue() {
        return (float) intValue();
    }

    @Override
    public int intValue() {
        return this.value;
    }

    @Override
    public long longValue() {
        return (long) intValue();
    }

    public String toString() {
        StringBuilder buff = new StringBuilder();

        TimePoint t = TimePoint.fromInt(value);
        buff.append(t.hours);
        buff.append(":");

        if (t.minutes < 10) {
            buff.append("0");
        }

        buff.append(t.minutes);
        buff.append(":");

        if (t.seconds < 10) {
            buff.append("0");
        }

        buff.append(t.seconds);

        return buff.toString();
    }

    /**
     * XXX Assumes StartTime was "today".
     * @return
     */
    public Date toWallClockTime() {
        TimePoint t = TimePoint.fromInt(value);

        Calendar date = GregorianCalendar.getInstance();

        short hours = t.hours;

        while (hours > 23) {
            date.add(Calendar.DAY_OF_YEAR, 1);
            hours -= 24;
        }

        date.set(Calendar.HOUR_OF_DAY, t.hours);
        date.set(Calendar.MINUTE, t.minutes);
        date.set(Calendar.SECOND, t.seconds);
        date.set(Calendar.MILLISECOND, 0);

        return date.getTime();
    }

    private static class TimePoint {
        final short hours;
        final short minutes;
        final short seconds;

        public TimePoint(short hours, short minutes, short seconds) {
            this.hours = hours;
            this.minutes = minutes;
            this.seconds = seconds;
        }

        static TimePoint fromInt(int value) {
            short hours = (short) (value / (60 * 60));
            int r = (value - (hours * 60 * 60));
            short minutes = (short) (r / 60);
            short seconds = (short) (r - (minutes * 60));

            return new TimePoint(hours, minutes, seconds);
        }

        static TimePoint parseTimePoint(String s) throws NumberFormatException {
            int idx;

            //XXX consider optimizing for known lengths instead of searching through.
            if ((idx = s.indexOf(':')) != -1) {
                //Trim off leading spaces from hour string, even though technically a leading space is invalid.
                short hours = Short.valueOf(s.substring(0, idx).trim()); //hours

                if ((idx = s.indexOf(':', idx + 1)) != -1) {
                    short minutes = Short.valueOf(s.substring(idx - 2, idx)); //minutes
                    short seconds = Short.valueOf(s.substring(idx + 1)); //seconds

                    return new TimePoint(hours, minutes, seconds);
                } else {
                    throw new NumberFormatException("String missing second ':' seperator:" + s);
                }
            } else {
                throw new NumberFormatException("String missing ':' seperator:" + s);
            }
        }
    }

    /**
     * Parses the string argument as a TripTimePoint.
     */
    public static TripTimePoint parseTripTimePoint(String s) throws NumberFormatException {
        TimePoint t = TimePoint.parseTimePoint(s);
        int value = (t.hours * 60 * 60 // hours into seconds
        ) + (t.minutes * 60 // minutes into seconds
        ) + t.seconds; // seconds

        return new TripTimePoint(value);
    }
}
